/*     */ package com.zimbra.cs.zimlet;
/*     */ 
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.httpclient.HttpClientUtil;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.mime.ContentDisposition;
/*     */ import com.zimbra.common.mime.ContentType;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraHttpConnectionManager;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.AuthToken;
/*     */ import com.zimbra.cs.account.AuthTokenException;
/*     */ import com.zimbra.cs.account.Cos;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.httpclient.HttpProxyUtil;
/*     */ import com.zimbra.cs.service.AuthProvider;
/*     */ import com.zimbra.cs.service.FileUploadServlet;
/*     */ import com.zimbra.cs.service.FileUploadServlet.Upload;
/*     */ import com.zimbra.cs.servlet.ZimbraServlet;
/*     */ import java.io.ByteArrayOutputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.net.URL;
/*     */ import java.util.Arrays;
/*     */ import java.util.Enumeration;
/*     */ import java.util.List;
/*     */ import java.util.Set;
/*     */ import javax.servlet.http.HttpServletRequest;
/*     */ import javax.servlet.http.HttpServletResponse;
/*     */ import org.apache.commons.httpclient.Header;
/*     */ import org.apache.commons.httpclient.HttpClient;
/*     */ import org.apache.commons.httpclient.HttpException;
/*     */ import org.apache.commons.httpclient.HttpMethod;
/*     */ import org.apache.commons.httpclient.HttpState;
/*     */ import org.apache.commons.httpclient.UsernamePasswordCredentials;
/*     */ import org.apache.commons.httpclient.auth.AuthScope;
/*     */ import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
/*     */ import org.apache.commons.httpclient.methods.DeleteMethod;
/*     */ import org.apache.commons.httpclient.methods.GetMethod;
/*     */ import org.apache.commons.httpclient.methods.PostMethod;
/*     */ import org.apache.commons.httpclient.methods.PutMethod;
/*     */ import org.apache.commons.httpclient.params.HttpMethodParams;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class ProxyServlet
/*     */   extends ZimbraServlet
/*     */ {
/*     */   private static final String TARGET_PARAM = "target";
/*     */   private static final String UPLOAD_PARAM = "upload";
/*     */   private static final String FILENAME_PARAM = "filename";
/*     */   private static final String FORMAT_PARAM = "fmt";
/*     */   private static final String USER_PARAM = "user";
/*     */   private static final String PASS_PARAM = "pass";
/*     */   private static final String AUTH_PARAM = "auth";
/*     */   private static final String AUTH_BASIC = "basic";
/*     */   private static final String DEFAULT_CTYPE = "text/xml";
/*     */   
/*     */   private Set<String> getAllowedDomains(AuthToken auth)
/*     */     throws ServiceException
/*     */   {
/*  83 */     Provisioning prov = Provisioning.getInstance();
/*  84 */     Account acct = prov.get(Key.AccountBy.id, auth.getAccountId(), auth);
/*     */     
/*  86 */     Cos cos = prov.getCOS(acct);
/*     */     
/*  88 */     Set<String> allowedDomains = cos.getMultiAttrSet("zimbraProxyAllowedDomains");
/*     */     
/*  90 */     ZimbraLog.zimlet.debug("get allowedDomains result: " + allowedDomains);
/*     */     
/*  92 */     return allowedDomains;
/*     */   }
/*     */   
/*     */   private boolean checkPermissionOnTarget(URL target, AuthToken auth) {
/*  96 */     String host = target.getHost().toLowerCase();
/*  97 */     ZimbraLog.zimlet.debug("checking allowedDomains permission on target host: " + host);
/*     */     Set<String> domains;
/*     */     try {
/* 100 */       domains = getAllowedDomains(auth);
/*     */     } catch (ServiceException se) {
/* 102 */       ZimbraLog.zimlet.info("error getting allowedDomains: " + se.getMessage());
/* 103 */       return false;
/*     */     }
/* 105 */     for (String domain : domains) {
/* 106 */       if (domain.equals("*")) {
/* 107 */         return true;
/*     */       }
/* 109 */       if (domain.charAt(0) == '*') {
/* 110 */         domain = domain.substring(1);
/*     */       }
/* 112 */       if (host.endsWith(domain)) {
/* 113 */         return true;
/*     */       }
/*     */     }
/* 116 */     return false;
/*     */   }
/*     */   
/*     */   private boolean canProxyHeader(String header) {
/* 120 */     if (header == null) return false;
/* 121 */     header = header.toLowerCase();
/* 122 */     if ((header.startsWith("accept")) || (header.equals("content-length")) || (header.equals("connection")) || (header.equals("keep-alive")) || (header.equals("pragma")) || (header.equals("host")) || (header.equals("cache-control")) || (header.equals("cookie")) || (header.equals("transfer-encoding")))
/*     */     {
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 132 */       return false;
/*     */     }
/* 134 */     return true;
/*     */   }
/*     */   
/*     */   private byte[] copyPostedData(HttpServletRequest req) throws IOException {
/* 138 */     int size = req.getContentLength();
/* 139 */     if ((req.getMethod().equalsIgnoreCase("GET")) || (size <= 0)) {
/* 140 */       return null;
/*     */     }
/* 142 */     InputStream is = req.getInputStream();
/* 143 */     ByteArrayOutputStream baos = null;
/*     */     try {
/* 145 */       if (size < 0)
/* 146 */         size = 0;
/* 147 */       baos = new ByteArrayOutputStream(size);
/* 148 */       byte[] buffer = new byte[' '];
/*     */       int num;
/* 150 */       while ((num = is.read(buffer)) != -1) {
/* 151 */         baos.write(buffer, 0, num);
/*     */       }
/* 153 */       return baos.toByteArray();
/*     */     } finally {
/* 155 */       ByteUtil.closeStream(baos);
/*     */     }
/*     */   }
/*     */   
/*     */   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException
/*     */   {
/* 161 */     doProxy(req, resp);
/*     */   }
/*     */   
/*     */   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException
/*     */   {
/* 166 */     doProxy(req, resp);
/*     */   }
/*     */   
/*     */   protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException
/*     */   {
/* 171 */     doProxy(req, resp);
/*     */   }
/*     */   
/*     */   protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException
/*     */   {
/* 176 */     doProxy(req, resp);
/*     */   }
/*     */   
/*     */   protected boolean isAdminRequest(HttpServletRequest req)
/*     */   {
/* 181 */     return req.getServerPort() == LC.zimbra_admin_service_port.intValue();
/*     */   }
/*     */   
/*     */   private void doProxy(HttpServletRequest req, HttpServletResponse resp)
/*     */     throws IOException
/*     */   {
/* 187 */     ZimbraLog.clearContext();
/* 188 */     boolean isAdmin = isAdminRequest(req);
/* 189 */     AuthToken authToken = isAdmin ? getAdminAuthTokenFromCookie(req, resp, true) : getAuthTokenFromCookie(req, resp, true);
/*     */     
/* 191 */     if (authToken == null) {
/* 192 */       String zAuthToken = req.getParameter("zauthtoken");
/* 193 */       if (zAuthToken != null) {
/*     */         try {
/* 195 */           authToken = AuthProvider.getAuthToken(zAuthToken);
/* 196 */           if (authToken.isExpired()) {
/* 197 */             resp.sendError(401, "authtoken expired");
/* 198 */             return;
/*     */           }
/* 200 */           if (!authToken.isRegistered()) {
/* 201 */             resp.sendError(401, "authtoken is invalid");
/* 202 */             return;
/*     */           }
/* 204 */           if ((isAdmin) && (!authToken.isAdmin())) {
/* 205 */             resp.sendError(401, "permission denied");
/* 206 */             return;
/*     */           }
/*     */         } catch (AuthTokenException e) {
/* 209 */           resp.sendError(401, "unable to parse authtoken");
/* 210 */           return;
/*     */         }
/*     */       }
/*     */     }
/* 214 */     if (authToken == null) {
/* 215 */       resp.sendError(401, "no authtoken cookie");
/* 216 */       return;
/*     */     }
/*     */     
/*     */ 
/* 220 */     byte[] body = copyPostedData(req);
/*     */     
/*     */ 
/* 223 */     String target = req.getParameter("target");
/* 224 */     if (target == null) {
/* 225 */       resp.sendError(400);
/* 226 */       return;
/*     */     }
/*     */     
/*     */ 
/* 230 */     URL url = new URL(target);
/* 231 */     if ((!isAdmin) && (!checkPermissionOnTarget(url, authToken))) {
/* 232 */       resp.sendError(403);
/* 233 */       return;
/*     */     }
/*     */     
/*     */ 
/* 237 */     String uploadParam = req.getParameter("upload");
/* 238 */     boolean asUpload = (uploadParam != null) && ((uploadParam.equals("1")) || (uploadParam.equalsIgnoreCase("true")));
/*     */     
/* 240 */     HttpMethod method = null;
/*     */     try {
/* 242 */       HttpClient client = ZimbraHttpConnectionManager.getExternalHttpConnMgr().newHttpClient();
/* 243 */       HttpProxyUtil.configureProxy(client);
/* 244 */       String reqMethod = req.getMethod();
/* 245 */       if (reqMethod.equalsIgnoreCase("GET")) {
/* 246 */         method = new GetMethod(target);
/* 247 */       } else if (reqMethod.equalsIgnoreCase("POST")) {
/* 248 */         PostMethod post = new PostMethod(target);
/* 249 */         if (body != null)
/* 250 */           post.setRequestEntity(new ByteArrayRequestEntity(body, req.getContentType()));
/* 251 */         method = post;
/* 252 */       } else if (reqMethod.equalsIgnoreCase("PUT")) {
/* 253 */         PutMethod put = new PutMethod(target);
/* 254 */         if (body != null)
/* 255 */           put.setRequestEntity(new ByteArrayRequestEntity(body, req.getContentType()));
/* 256 */         method = put;
/* 257 */       } else if (reqMethod.equalsIgnoreCase("DELETE")) {
/* 258 */         method = new DeleteMethod(target);
/*     */       } else {
/* 260 */         ZimbraLog.zimlet.info("unsupported request method: " + reqMethod);
/* 261 */         resp.sendError(405); return;
/*     */       }
/*     */       
/*     */ 
/*     */ 
/*     */ 
/* 267 */       String auth = req.getParameter("auth");
/* 268 */       String user = req.getParameter("user");
/* 269 */       String pass = req.getParameter("pass");
/* 270 */       if ((auth != null) && (user != null) && (pass != null)) {
/* 271 */         if (!auth.equals("basic")) {
/* 272 */           ZimbraLog.zimlet.info("unsupported auth type: " + auth);
/* 273 */           resp.sendError(400); return;
/*     */         }
/*     */         
/* 276 */         HttpState state = new HttpState();
/* 277 */         state.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, pass));
/* 278 */         client.setState(state);
/* 279 */         method.setDoAuthentication(true);
/*     */       }
/*     */       
/* 282 */       Enumeration headers = req.getHeaderNames();
/* 283 */       while (headers.hasMoreElements()) {
/* 284 */         String hdr = (String)headers.nextElement();
/* 285 */         ZimbraLog.zimlet.debug("incoming: " + hdr + ": " + req.getHeader(hdr));
/* 286 */         if (canProxyHeader(hdr)) {
/* 287 */           ZimbraLog.zimlet.debug("outgoing: " + hdr + ": " + req.getHeader(hdr));
/* 288 */           if (hdr.equalsIgnoreCase("x-host")) {
/* 289 */             method.getParams().setVirtualHost(req.getHeader(hdr));
/*     */           } else {
/* 291 */             method.addRequestHeader(hdr, req.getHeader(hdr));
/*     */           }
/*     */         }
/*     */       }
/*     */       try {
/* 296 */         if ((!reqMethod.equalsIgnoreCase("POST")) && (!reqMethod.equalsIgnoreCase("PUT"))) {
/* 297 */           method.setFollowRedirects(true);
/*     */         }
/* 299 */         HttpClientUtil.executeMethod(client, method);
/*     */       } catch (HttpException ex) {
/* 301 */         ZimbraLog.zimlet.info("exception while proxying " + target, ex);
/* 302 */         resp.sendError(404); return;
/*     */       }
/*     */       
/*     */ 
/* 306 */       int status = method.getStatusLine() == null ? 500 : method.getStatusCode();
/*     */       
/*     */ 
/* 309 */       Header ctHeader = method.getResponseHeader("Content-Type");
/* 310 */       String contentType = (ctHeader == null) || (ctHeader.getValue() == null) ? "text/xml" : ctHeader.getValue();
/*     */       
/* 312 */       InputStream targetResponseBody = method.getResponseBodyAsStream();
/*     */       
/* 314 */       if (asUpload) {
/* 315 */         String filename = req.getParameter("filename");
/* 316 */         if ((filename == null) || (filename.equals("")))
/* 317 */           filename = new ContentType(contentType).getParameter("name");
/* 318 */         if (((filename == null) || (filename.equals(""))) && (method.getResponseHeader("Content-Disposition") != null))
/* 319 */           filename = new ContentDisposition(method.getResponseHeader("Content-Disposition").getValue()).getParameter("filename");
/* 320 */         if ((filename == null) || (filename.equals(""))) {
/* 321 */           filename = "unknown";
/*     */         }
/* 323 */         List<FileUploadServlet.Upload> uploads = null;
/*     */         
/* 325 */         if (targetResponseBody != null) {
/*     */           try {
/* 327 */             FileUploadServlet.Upload up = FileUploadServlet.saveUpload(targetResponseBody, filename, contentType, authToken.getAccountId());
/* 328 */             uploads = Arrays.asList(new FileUploadServlet.Upload[] { up });
/*     */           } catch (ServiceException e) {
/* 330 */             if (e.getCode().equals("mail.UPLOAD_REJECTED")) {
/* 331 */               status = 413;
/*     */             } else {
/* 333 */               status = 500;
/*     */             }
/*     */           }
/*     */         }
/* 337 */         resp.setStatus(status);
/* 338 */         FileUploadServlet.sendResponse(resp, status, req.getParameter("fmt"), null, uploads, null);
/*     */       } else {
/* 340 */         resp.setStatus(status);
/* 341 */         resp.setContentType(contentType);
/* 342 */         for (Header h : method.getResponseHeaders())
/* 343 */           if (canProxyHeader(h.getName()))
/* 344 */             resp.addHeader(h.getName(), h.getValue());
/* 345 */         if (targetResponseBody != null)
/* 346 */           ByteUtil.copy(targetResponseBody, true, resp.getOutputStream(), true);
/*     */       }
/*     */     } finally {
/* 349 */       if (method != null) {
/* 350 */         method.releaseConnection();
/*     */       }
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/zimlet/ProxyServlet.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */